package com.atguigu.srb.core.controller.api;


import com.alibaba.fastjson.JSON;
import com.atguigu.srb.base.asserts.Asserts;
import com.atguigu.srb.base.response.R;
import com.atguigu.srb.base.response.ResponseEnum;
import com.atguigu.srb.base.util.JwtUtils;
import com.atguigu.srb.core.hfb.FormHelper;
import com.atguigu.srb.core.hfb.HfbConst;
import com.atguigu.srb.core.hfb.LendNoUtils;
import com.atguigu.srb.core.hfb.RequestHelper;
import com.atguigu.srb.core.pojo.entity.TransFlow;
import com.atguigu.srb.core.pojo.entity.UserAccount;
import com.atguigu.srb.core.pojo.entity.UserInfo;
import com.atguigu.srb.core.service.TransFlowService;
import com.atguigu.srb.core.service.UserAccountService;
import com.atguigu.srb.core.service.UserInfoService;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;

/**
 * <p>
 * 用户账户 前端控制器
 * </p>
 *
 * @author Atguigu
 * @since 2023-05-26
 */
@RestController
@Slf4j
@RequestMapping("/api/core/userAccount")
public class ApiUserAccountController {

    @Resource
    UserInfoService userInfoService;
    @Resource
    TransFlowService transFlowService;

    @Resource
    UserAccountService userAccountService;
    //http://192.168.1.170/api/core/userAccount/notifyWithdraw
    ///api/core/userAccount/notifyWithdraw
    @ApiOperation("会员提现成功的回调接口")
    @PostMapping("/notifyWithdraw")
    public String notifyWithdraw(HttpServletRequest request){

        return userAccountService.notifyWithdraw(request);
    }
    @ApiOperation("提现申请")
    @PostMapping("auth/withDraw")
    public R withDraw(@RequestHeader("token")String token,
                      @RequestParam("amount")BigDecimal amount){
        //返回自动提交的表单字符串
        String form = userAccountService.withDraw(token,amount);
        return R.ok().data("form",form);
    }



    //根据还款方式选择工具类 传入 投资金额、期数、年化率 计算收益
    @ApiOperation("计算收益")
    @GetMapping("interestCount/{invest}/{period}/{yearRate}/{returnMethod}")
    public R getInterestCount(@PathVariable("invest")BigDecimal invest,
                              @PathVariable("period")Integer period,
                              @PathVariable("yearRate")BigDecimal yearRate,
                              @PathVariable("returnMethod")Integer returnMethod){
        BigDecimal interestCount = userAccountService
                .getInterestCount(invest,period,yearRate,returnMethod);

        return R.ok().data("amount",interestCount);
    }


    @ApiOperation("查询账户余额")
    @GetMapping("auth/amount")
    public R getAmount(@RequestHeader("token")String token){
        BigDecimal amount = userAccountService.getAmount(token);
        return R.ok().data("amount",amount);
    }

    @ApiOperation("充值")
    @PostMapping("auth/recharge")
    public R recharge(@RequestHeader("token")String token,
                      @RequestParam("amount")BigDecimal amount){
        //校验账户是否可用
        UserInfo userInfo = userInfoService.checkAccount(token);
        Long userId = userInfo.getId();
//        UserInfo userInfo = userInfoService.getById(userId);
        //充值金额必须大于0
        Asserts.assertNotTrue(amount.compareTo(new BigDecimal(0))<=0 ,
                ResponseEnum.CHARGE_AMOUNT_ERROR);

        //1、准备参数:  bindCode- 可以通过token来获取  ， amount- 前端表单数据提交
        Map<String, Object> params = new HashMap<>();
        // 应用在hfb中注册时分配的唯一id：
        params.put("agentId", HfbConst.AGENT_ID);
        // 本次充值的唯一编号： 我们自己生成
        String chargeNo = LendNoUtils.getChargeNo();
        // 前端提交的请求 保存数据到数据库时，请求中如果有一个唯一的字符串， 可以对存该字符串的表字段添加唯一约束
        params.put("agentBillNo", chargeNo);
        //会员在hfb中绑定时的 唯一编号
        params.put("bindCode",userInfo.getBindCode());
        //充值金额
        params.put("chargeAmt",amount);
        // 手续费：
        params.put("feeAmt",0);
        // 异步回调地址： 充值成功回调接口地址 可以接受到会员充值的结果
        //注意：异步回调地址 常量值必须改为虚拟机ip地址
        params.put("notifyUrl",HfbConst.RECHARGE_NOTIFY_URL);
        params.put("returnUrl",HfbConst.RECHARGE_RETURN_URL);
        params.put("timestamp",System.currentTimeMillis());
        //签名: 一定要在参数设置完以后再生成
        params.put("sign",RequestHelper.getSign(params));
        //参数1：hfb充值接口地址  参数2：表单参数
        String form = FormHelper.buildForm(HfbConst.RECHARGE_URL, params);

        //保存交易流水日志：状态为未成功
        transFlowService.extracted(amount, userId, userInfo, chargeNo,"充值");


        //2、根据参数构建自动提交的表单响应
        return R.ok().data("form",form);
    }



    //http://192.168.1.170/api/core/userAccount/notify
    @ApiOperation("充值回调：hfb访问")
    @PostMapping("notify")
    public String chargeNotify(HttpServletRequest request){
        //回调时 hfb传入的参数较多，通过工具类解析参数
        Map<String, Object> map = RequestHelper.switchMap(request.getParameterMap());
        //1、验签：防止数据被篡改
        if(!RequestHelper.isSignEquals(map)){
            //记录日志
            log.error("充值回调,验签失败：{}", JSON.toJSONString(map));
            //给第三方hfb响应失败
            return "fail";
        }
        //2、校验充值响应状态码是否成功
        if(!StringUtils.equals("0001",map.get("resultCode").toString())){
            //状态码错误
            log.error("充值回调,hfb处理失败：{}", JSON.toJSONString(map));
            //给第三方hfb响应失败
            return "fail";
        }
        //3、校验实际充值成功的金额是否正确:需要使用上一步接收到的充值金额
//        map.get("chargeAmt")
        //获取agentBillNo 再到交易日志表中获取对应的日志可以查询到充值金额
        String agentBillNo = map.get("agentBillNo").toString();
        TransFlow transFlow = transFlowService.getTransFlowByAgentBillNo(agentBillNo);
        //如果交易流水状态为成功, 后续代码不再执行  保证幂等性
        if(transFlow.getStatus()==1){
            log.error("充值成功：{}", JSON.toJSONString(map));
            return "success";
        }

        BigDecimal chargeAmt = new BigDecimal(map.get("chargeAmt").toString());
        if(transFlow.getTransAmount().compareTo(chargeAmt)!=0){
            //状态码错误
            log.error("充值回调,充值金额不正确：{}", JSON.toJSONString(map));
            //给第三方hfb响应失败
            return "fail";
        }
        //4、全部校验通过：更新会员的账户余额增加本次充值金额、更新交易流水日志
        //获取会员账户信息 更新: 根据bindCode查询会员账户  必须先查询userId 再查询会员账户
        String bindCode = map.get("bindCode").toString();
        UserAccount userAccount = userAccountService.getUserAccountByBindCode(bindCode);
        userAccount.setAmount(userAccount.getAmount().add(chargeAmt));
        userAccountService.updateById(userAccount);

        //更新交易日志: 更新为成功的状态
        transFlow.setStatus(1);
        transFlow.setMemo(JSON.toJSONString(map));
        transFlowService.updateById(transFlow);
        log.error("充值成功：{}", JSON.toJSONString(map));
        return "success";
    }

}

